﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using XNA.Common.Objects;

namespace XNA.Common.Map
{
	public static class MapLoader
	{
		public static Map LoadMap (Texture2D map, List<Texture2D> texture, List<ElementData> mapElements, int pLen, int roomHeight)
		{
			var width = map.Bounds.Width;
			var height = map.Bounds.Height;

			// Wtf mate, clean this shit up!
			Color[] tempBitmap = new Color[width * height];
			map.GetData (tempBitmap);
			Color[,] bitmap = new Color[width, height];

			for (int i = 0; i < width; i++)
			{
				for (int j = 0; j < height; j++)
				{
					bitmap[i, j] = tempBitmap[i + j * width];
				}
			}

			var hScaling = roomHeight / pLen;

			var textureDictionary = new Dictionary<Color, TexturedVertices> ();
			var elementDictionary = new Dictionary<Color, List<Entity>> ();
			var textureIndex = 2;

			var spaceColor = bitmap[0, 0];
			var floorColor = bitmap[1, 0];
			textureDictionary.Add (floorColor, new TexturedVertices (texture.First ()));
			textureDictionary.Add (spaceColor, new TexturedVertices (texture.Skip (1).First ()));

			for (int i = 2; i < width; i++)
			{
				var pixel = bitmap[i, 0];

				if (pixel == spaceColor || textureDictionary.ContainsKey(pixel))
					break;

				if (!textureDictionary.ContainsKey (pixel))
				{
					textureDictionary.Add (pixel, new TexturedVertices (texture[textureIndex++]));
				}
			}

			var elementColors = mapElements.Select (x => x.Color);

			//foreach (var id in bitmap.PropertyIdList)
			//{
			//    var propertyItem = bitmap.GetPropertyItem (id);
			//}

			for (int y = 1; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					var pixel = bitmap[x, y];
					if (pixel == floorColor || elementColors.Contains (pixel))
					{
						// Floor
						textureDictionary[floorColor].Vertices.AddRange (VertexHelper.CreateRectangle (
							new Vector3 (0, 1, 0), 1, 1,
							new Vector3 (x * pLen, 0, y * pLen),
							new Vector3 ((x + 1) * pLen, 0, y * pLen),
							new Vector3 ((x + 1) * pLen, 0, (y + 1) * pLen),
							new Vector3 (x * pLen, 0, (y + 1) * pLen)));

						// Ceiling
						textureDictionary[spaceColor].Vertices.AddRange (VertexHelper.CreateRectangle (
							new Vector3 (0, -1, 0), 1, 1,
							new Vector3 ((x + 1) * pLen, roomHeight, y * pLen),
							new Vector3 (x * pLen, roomHeight, y * pLen),
							new Vector3 (x * pLen, roomHeight, (y + 1) * pLen),
							new Vector3 ((x + 1) * pLen, roomHeight, (y + 1) * pLen)));

						// Left wall
						if (x > 0)
						{
							pixel = bitmap[x - 1, y];
							{
								if (pixel != floorColor)
								{
									if (pixel == spaceColor)
									{
										throw new InvalidOperationException (string.Format ("Floor is unbounded at ({0}, {1})", x - 1, y));
									}

									TexturedVertices texDict;
									if (textureDictionary.TryGetValue (pixel, out texDict))
									{
										texDict.Vertices.AddRange (VertexHelper.CreateRectangle (
											new Vector3 (1, 0, 0), 1, hScaling,
											new Vector3 (x * pLen, 0, y * pLen),
											new Vector3 (x * pLen, 0, (y + 1) * pLen),
											new Vector3 (x * pLen, roomHeight, (y + 1) * pLen),
											new Vector3 (x * pLen, roomHeight, y * pLen)));
									}
									else if (elementColors.Contains (pixel) == false)
									{
										throw new InvalidOperationException (string.Format ("Unexpected color at ({0}, {1})", x - 1, y));
									}
								}
							}
						}

						// Above wall
						if (y > 1)
						{
							pixel = bitmap[x, y - 1];
							{
								if (pixel != floorColor)
								{
									if (pixel == spaceColor)
									{
										throw new InvalidOperationException (string.Format ("Floor is unbounded at ({0}, {1})", x, y - 1));
									}

									TexturedVertices texDict;
									if (textureDictionary.TryGetValue (pixel, out texDict))
									{
										texDict.Vertices.AddRange (VertexHelper.CreateRectangle (
											new Vector3 (0, 0, 1), 1, hScaling,
											new Vector3 ((x + 1) * pLen, 0, y * pLen),
											new Vector3 (x * pLen, 0, y * pLen),
											new Vector3 (x * pLen, roomHeight, y * pLen),
											new Vector3 ((x + 1) * pLen, roomHeight, y * pLen)));
									}
									else if (elementColors.Contains (pixel) == false)
									{
										throw new InvalidOperationException (string.Format ("Unexpected color at ({0}, {1})", x, y - 1));
									}
								}
							}
						}

						// Right wall
						if (x < width - 1)
						{
							pixel = bitmap[x + 1, y];
							{
								if (pixel != floorColor)
								{
									if (pixel == spaceColor)
									{
										throw new InvalidOperationException (string.Format ("Floor is unbounded at ({0}, {1})", x + 1, y));
									}

									TexturedVertices texDict;
									if (textureDictionary.TryGetValue (pixel, out texDict))
									{
										texDict.Vertices.AddRange (VertexHelper.CreateRectangle (
											new Vector3 (-1, 0, 0), 1, hScaling,
											new Vector3 ((x + 1) * pLen, 0, (y + 1) * pLen),
											new Vector3 ((x + 1) * pLen, 0, y * pLen),
											new Vector3 ((x + 1) * pLen, roomHeight, y * pLen),
											new Vector3 ((x + 1) * pLen, roomHeight, (y + 1) * pLen)));
									}
									else if (elementColors.Contains (pixel) == false)
									{
										throw new InvalidOperationException (string.Format ("Unexpected color at ({0}, {1})", x + 1, y));
									}
								}
							}
						}

						// Below wall
						if (y < height - 1)
						{
							pixel = bitmap[x, y + 1];
							{
								if (pixel != floorColor)
								{
									if (pixel == spaceColor)
									{
										throw new InvalidOperationException (string.Format ("Floor is unbounded at ({0}, {1})", x, y + 1));
									}

									TexturedVertices texDict;
									if (textureDictionary.TryGetValue (pixel, out texDict))
									{
										texDict.Vertices.AddRange (VertexHelper.CreateRectangle (
											new Vector3 (0, 0, -1), 1, hScaling,
											new Vector3 (x * pLen, 0, (y + 1) * pLen),
											new Vector3 ((x + 1) * pLen, 0, (y + 1) * pLen),
											new Vector3 ((x + 1) * pLen, roomHeight, (y + 1) * pLen),
											new Vector3 (x * pLen, roomHeight, (y + 1) * pLen)));
									}
									else if (elementColors.Contains (pixel) == false)
									{
										throw new InvalidOperationException (string.Format ("Unexpected color at ({0}, {1})", x, y + 1));
									}
								}
							}
						}

						// Map Element
						pixel = bitmap[x, y];
						if (pixel != floorColor)
						{
							var mapElement = mapElements.Single (e => e.Color == pixel);

							List<Entity> elementList;
							if (elementDictionary.TryGetValue (pixel, out elementList) == false)
							{
								elementList = new List<Entity> ();
								elementDictionary.Add (pixel, elementList);
							}

							var xs = x + 0.5f;
							var ys = y + 0.5f;
							var rot = 0.0f;

							if (mapElement.ClingToWall)
							{
								if (textureDictionary.Keys.Skip(2).Contains (bitmap[x - 1, y]))
								{
									xs = x;
									ys = y + 0.5f;
									rot = MathHelper.Pi;
								}
								if (textureDictionary.Keys.Skip (2).Contains (bitmap[x + 1, y]))
								{
									xs = x + 1;
									ys = y + 0.5f;
									rot = 0;
								}
								if (textureDictionary.Keys.Skip (2).Contains (bitmap[x, y - 1]))
								{
									xs = x + 0.5f;
									ys = y;
									rot = MathHelper.PiOver2;
								}
								if (textureDictionary.Keys.Skip (2).Contains (bitmap[x, y + 1]))
								{
									xs = x + 0.5f;
									ys = y + 1;
									rot = -MathHelper.PiOver2;
								}
							}

							elementList.Add (new Light (
								Color.Yellow,
								xs * pLen,
								roomHeight / 2,
								ys * pLen,
								0, // TODO: Height.
								rot,
								mapElement.Model));
						}
					}
				}
			}

			return new Map ()
			{
				Vertices = textureDictionary.Values,
				Elements = elementDictionary.Values.SelectMany (x => x),
			};
		}
	}
}
